package com.ccp.dev.workflow.service.bpm;
import	java.util.ArrayList;
import com.ccp.dev.core.basic.base.BaseService;
import com.ccp.dev.core.basic.consts.SystemConst;
import com.ccp.dev.core.basic.util.ContextUtil;
import com.ccp.dev.core.basic.util.UUIDUtils;
import com.ccp.dev.core.util.BeanUtils;
import com.ccp.dev.core.util.StringUtil;
import com.ccp.dev.system.model.SysUser;
import com.ccp.dev.system.service.SysUserService;
import com.ccp.dev.workflow.bpmconst.BpmConst;
import com.ccp.dev.workflow.dao.TaskSignDataDao;
import com.ccp.dev.workflow.model.ProcessRun;
import com.ccp.dev.workflow.model.bpm.BpmRunLog;
import com.ccp.dev.workflow.model.bpm.ProcessTask;
import com.ccp.dev.workflow.model.bpm.TaskExecutor;
import com.ccp.dev.workflow.model.bpm.TaskSignData;
import com.ccp.dev.workflow.service.BpmService;
import com.ccp.dev.workflow.service.ProcessRunService;
import org.flowable.bpmn.model.Activity;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.MultiInstanceLoopCharacteristics;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ClassDescribe:
 * 任务会签数据
 * @author :wangcheng
 * Date: 2019-09-06
 */
@Service
public class TaskSignDataService extends BaseService<TaskSignData> {
    @Resource
    private TaskSignDataDao taskSignDataDao;
    @Resource
    private BpmService bpmService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private ProcessRunService processRunService;
    @Resource
    private BpmRunLogService bpmRunLogService;
    @Resource
    private SysUserService sysUserService;

    /**
     * 取得某个流程某个节点所有的投同意票的总数
     * @param procInstId 流程实例Id
     * @param nodeId 节点Id
     * @return
     */
    public Integer getAgreeVoteCount(String procInstId, String nodeId) {
        return taskSignDataDao.getAgreeVoteCount(procInstId, nodeId);
    }

    /**
     * 取得某个流程某个节点所有的投反对票的总数
     * @param procInstId 流程实例Id
     * @param nodeId 节点Id
     * @return
     */
    public Integer getRefuseVoteCount(String procInstId, String nodeId) {
        return taskSignDataDao.getRefuseVoteCount(procInstId, nodeId);
    }

    /**
     * 更新本次会签投票完成的状态
     * @param procInstId 流程实例Id
     * @param nodeId 节点Id
     */
    public void batchUpdateCompleted(String procInstId, String nodeId) {
        taskSignDataDao.batchUpdateCompleted(procInstId, nodeId);
    }

    /**
     * 根据流程实例id和节点id返回会签人员数据
     * @param procInstId 流程实例id
     * @param nodeId 节点id
     * @return 会签数据列表
     */
    public List<TaskSignData> queryByNodeAndInstanceId(String procInstId, String nodeId) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("procInstId", procInstId);
        params.put("nodeId", nodeId);
        return taskSignDataDao.queryByNodeAndInstanceId(params);
    }

    /**
     * 某个任务实例的一次会签投票
     * <pre>
     * 获取会签数据，并更新投票的状态，意见，日期等内容。
     * </pre>
     * @param taskId 任务id
     * @param voteContent 投票内容
     * @param voteAgree 投票意见
     */
    public void signVoteTask(String taskId, String voteContent, Short voteAgree) {
        SysUser sysUser = (SysUser) ContextUtil.getCurrentUser();
        String userId = SystemConst.SYSTEMUSERID;
        String fullName = SystemConst.SYSTEMUSERNAME;
        if (BeanUtils.isNotEmpty(sysUser)) {
            userId = sysUser.getUserId();
            fullName = sysUser.getFullName();
        }

        TaskSignData taskSignData = taskSignDataDao.getByTaskId(taskId);
        if (BeanUtils.isNotEmpty(taskSignData)) {
            taskSignData.setIsAgree(voteAgree);
            taskSignData.setContent(voteContent);
            taskSignData.setVoteTime(new Date());
            taskSignData.setVoteUserId(userId);
            taskSignData.setVoteUserName(fullName);
            update(taskSignData);
        }
    }

    /**
     * 取得最大的批次
     * @param procInstId 流程实例id
     * @param nodeId 节点id
     * @return
     */
    public Integer getMaxBatch(String procInstId, String nodeId) {
        return taskSignDataDao.getMaxBatch(procInstId, nodeId);
    }

    /**
     * 根据参数获取会签数据。
     * @param procInstId
     * @param nodeId
     * @param executorId
     * @return
     */
    public TaskSignData getUserTaskSign(String procInstId, String nodeId, String executorId) {
        return taskSignDataDao.getUserTaskSign(procInstId, nodeId, executorId);
    }

    /**
     * 增加会签人员
     * <pre>
     * 增加会签分为两种方式处理。
     * 1.串行会签。
     * 2.并行会签。
     * </pre>
     * @param signUserIds 补签人员
     * @param taskId 任务id
     * @param opinion 审批意见
     * @param informType 消息类型
     */
    @Transactional(rollbackFor = Exception.class)
    public void addSign(String signUserIds, String taskId, String opinion, String informType) throws Exception {
        // 用户为空
        if (StringUtil.isEmpty(signUserIds)) {
            return;
        }

        // 检查当前任务是否为会签任务
        TaskEntityImpl task = bpmService.getTask(taskId);
        String nodeId = task.getTaskDefinitionKey();
        ExecutionEntity execution = bpmService.getExecution(task.getExecutionId());

        boolean isSignTask = bpmService.isSignTask(task.getProcessDefinitionId(), nodeId);

        if (!isSignTask) {
            return;
        }

        String procInstId = task.getProcessInstanceId();
        // 获取最大的批次号
        Integer maxSignNums = taskSignDataDao.getMaxSignNums(procInstId, nodeId, TaskSignData.NOT_COMPLETED);

        Integer signNums = maxSignNums == null ? 1 : maxSignNums;
        List<String> uidList = new ArrayList<>();

        Map<String, Object> params = new HashMap<>(3);
        params.put("procInstId", procInstId);
        params.put("nodeId", nodeId);
        params.put("isCompleted", 0);
        List<TaskSignData> existTaskSignDatas = taskSignDataDao.queryByNodeAndInstanceId(params);

        Integer curBatch = 1;
        for (TaskSignData taskSignData : existTaskSignDatas) {
            curBatch = taskSignData.getBatch();
            uidList.add(taskSignData.getVoteUserId());
        }

        String[] uIds = signUserIds.split("[,]");

        // 获取可以添加的用户
        List<String> addUsers = getCanAddUsers(uidList, uIds);

        // 添加用户的个数
        int userAmount = addUsers.size();

        Map<String, String> userTaskMap = new HashMap<>();

        // 更新会签总实例数
        Integer nrOfInstances = (Integer) runtimeService.getVariable(task.getExecutionId(), "nrOfInstances");
        if (nrOfInstances != null) {
            runtimeService.setVariable(task.getExecutionId(), "nrOfInstances", nrOfInstances + userAmount);
        }

        FlowElement flowElement = bpmService.getFlowElement(task.getProcessDefinitionId(), nodeId);
        MultiInstanceLoopCharacteristics loopCharacteristics = ((Activity)flowElement).getLoopCharacteristics();
        boolean isSequential = loopCharacteristics.isSequential();


        // 串行
        String executionId = execution.getId();
        if (isSequential) {
            String varName = nodeId + "_" + BpmConst.SIGN_USERIDS;
            List<TaskExecutor> addList = new ArrayList<>();
            for (int i = 0; i < userAmount; i++) {
                String userId = addUsers.get(i);
                int sn = signNums + 1;
                addSignData("", nodeId, task.getName(), procInstId, sn, userId, curBatch);
                addList.add(TaskExecutor.getTaskUser(userId, ""));
                userTaskMap.put(userId, "");
            }
            List<TaskExecutor> executorList = (List<TaskExecutor>) runtimeService.getVariable(executionId, varName);
            executorList.addAll(addList);
            runtimeService.setVariable(executionId, varName, executorList);

        }
        // 并行
        else{
            Integer loopCounter=(Integer)runtimeService.getVariable(executionId, "loopCounter");
            //添加活动的线程个数
            Integer nrOfActiveInstances=(Integer)runtimeService.getVariable(executionId, "nrOfActiveInstances");
            runtimeService.setVariable(executionId, "nrOfActiveInstances", nrOfActiveInstances + userAmount);

            for(int i=0;i<userAmount;i++){
                String userId=addUsers.get(i);
                ProcessTask newProcessTask=bpmService.newTask(taskId,addUsers.get(i));
                String newExecutionId= newProcessTask.getExecutionId();

                runtimeService.setVariableLocal(newExecutionId, "loopCounter", loopCounter + i +1);
                runtimeService.setVariableLocal(newExecutionId, "assignee", TaskExecutor.getTaskUser(userId, "") );
                //添加会签数据
                int sn=signNums+1;
                addSignData(newProcessTask.getId(), execution.getActivityId(), task.getName(), procInstId, sn, userId, curBatch);
            }
        }

        // 写入流程运行日志
        ProcessRun processRun = processRunService.getByProcInstanceId(procInstId);
        // 处理没有填写补签意见以及流程对外服务接口时的情况
        if (StringUtil.isNotEmpty(opinion)) {
            // 添加审批意见
            processRunService.saveAddSignOpinion(task, opinion, informType, userTaskMap, processRun.getSubject());
        }
        String memo="用户在任务["+task.getName()+"]执行了补签操作。";
        bpmRunLogService.addRunLog(processRun.getRunId(), BpmRunLog.OPERATOR_TYPE_SIGN, memo);
    }

    /**
     * 添加会签数据
     * @param taskId 任务id
     * @param nodeId 节点id
     * @param name 节点名称
     * @param procInstId 流程实例id
     * @param signNums 会签人数
     * @param userId 用户id
     * @param curBatch 当前批次
     */
    private void addSignData(String taskId, String nodeId, String name, String procInstId, int signNums, String userId, Integer curBatch) {
        String dataId = UUIDUtils.getUUIDFor32();
        TaskSignData newSignData=new TaskSignData();
        newSignData.setTaskId(taskId);

        newSignData.setDataId(dataId);
        newSignData.setNodeId(nodeId);
        newSignData.setNodeName(name);
        newSignData.setProcInstId(procInstId);
        newSignData.setSignNums(signNums);
        newSignData.setIsCompleted(TaskSignData.NOT_COMPLETED);
        newSignData.setIsAgree(null);
        newSignData.setBatch(curBatch);
        newSignData.setContent(null);
        newSignData.setVoteTime(null);
        newSignData.setVoteUserId(userId);

        SysUser sysUser=sysUserService.getById(userId);
        newSignData.setVoteUserName(sysUser.getFullName());
        //添加
        taskSignDataDao.add(newSignData);
    }

    /**
     * 获取可以添加的用户列表
     * @param curUserList 当前用户id列表
     * @param addUsers 候选id数组
     * @return
     */
    private List<String> getCanAddUsers(List<String> curUserList, String[] addUsers) {
        List<String> users = new ArrayList<>();
        for (String userId : addUsers) {
            if (!curUserList.contains(userId)) {
                users.add(userId);
            }
        }
        return users;
    }

    /**
     * 根据任务id获取会签数据
     * @param taskId 任务id
     * @return 会签数据
     */
    public TaskSignData getByTaskId(String taskId) {
        return taskSignDataDao.getByTaskId(taskId);
    }
}
